﻿using System;
using System.Linq;
using CardPlay.Model;
using CardPlay.Model.ActionModel;
using CardPlay.Model.CardModel;
using CardPlay.Model.Primitives;
using CardPlay.Model.GameModel;
using CardPlay.Model.GameModel.Actions;
using CardPlay.Model.GameModel.Presentation;
using CardPlay.Model.Layout;

namespace CardPlay.DemoPack.Games.SpiderVariants
{
    [Game("A0DAA896-2428-4BA9-B231-AAF442B126D7", LevelOfDifficulty.Easy, LevelOfSkill.ModerateSkill, DurationOfPlay.Within5To10Minutes, Family = FamilyName)]
    public class Spiderette : StandardCardGame
    {
        public const string FamilyName = "Spider";

        private const string StockOrAnyTableau = "(stock|tableau.*)";

        public Spiderette()
        {
            NamesOfAdditionalPiles.AddRange(new[] {Stock, Waste});

            MaximumDeals = 1;
            TotalDecks = 1;
            TotalTableaus = 7;
            TotalFoundations = 0;
            TableauBuildSequence = CardFaceSequence.SameSuitDescendingRank;
        }

        public override ILayoutSite CreatePresentation(IPresentationContext presentation)
        {
            var metrics = presentation.Metrics;

            var stockStyle = CreateStockStyle(metrics);
            stockStyle.Arrangement = StackTower;
            stockStyle.EmptyCue = null;
            stockStyle.MinimumSize = new Size(metrics.CardSize.Width * 3 / 2, metrics.CardSize.Height);

            var tableau = presentation.CreateRowLayout(); // CreateSideBySideLayout(metrics);

            foreach (var pile in PilesNamed(AnyTableau))
                tableau.Add(presentation.CreatePileVisual(pile.Name, StandardStyles.Tableau));

            var root = new SideBySideLayout
                       {
                           VerticalContentAlignment = VerticalAlignment.Stretch
                       };

            root.Add(presentation.CreatePileVisual(Stock, stockStyle));
            root.Add(tableau);

            return root;
        }

        public override int PercentComplete
        {
            get
            {
                var cardCount = PilesNamed(StockOrAnyTableau).Aggregate(0, (total, pile) => total + pile.Count);
                var totalCards = TotalDecks * CardFace.CardsPerStandardDeck;

                return (totalCards - cardCount) * 100 / totalCards;
            }
        }

        protected override bool IsComplete
        {
            get
            {
                return this[Stock].IsEmpty && PilesNamed(AnyTableau).All(pile => pile.IsEmpty);
            }
        }

        protected override void PrepareDealScript(DealScript script)
        {
            script.Deal("up").To(Tableau1);
            script.Deal("down;up").To(Tableau2);
            script.Deal("down;down;up").To(Tableau3);
            script.Deal("down;down;down;up").To(Tableau4);
            script.Deal("down;down;down;down;up").To(Tableau5);
            script.Deal("down;down;down;down;down;up").To(Tableau6);
            script.Deal("down;down;down;down;down;down;up").To(Tableau7);
            script.Deal(";;;;;;;24 down").To(Stock);
        }

        protected override bool AllowedToPickupAtTableau(RemovePileAction action)
        {
            return action.IsActingOnFaceUpTopPileInSequence(TableauBuildSequence);
        }

        protected override bool AllowedToBuildNonEmptyTableau(MovePileAction action)
        {
            return action.TargetPile.TopCard.Rank != FoundationBaseRank && action.IsBuilding(TableauBuildSequence);
        }

        protected override IAction QueryActionAt(PartLocation location)
        {
            var card = CardAt(location);

            if (card == null) return null;

            if (location.Matches(Stock) && location.Index == this[Stock].TopIndex)
                return this.AnyEmptyPilesNamed(AnyTableau) ? null : new DealOutTableauAction(this, location);

            if (card.IsFaceUp)
            {
                var removeAction = new RemovePileAction(this, location);

                if (removeAction.PileBeingRemoved.Count == CardFace.RanksPerSuit &&
                    removeAction.PileBeingRemoved.IsInSequence(CardFaceSequence.SameSuitDescendingRank))
                    return new MovePileAction(this, removeAction, new PartLocation(Waste));
            }

            return base.QueryActionAt(location);
        }

        protected override void OnTransactionEnding()
        {
            this.FlipFaceDownTopCardsInPilesMatching(AnyTableau);
        }

        private Size[] StackTower(CardPile pile, LayoutMetrics metrics)
        {
            Guard.Against<ArgumentNullException>(pile == null, "pile");

            var stackingOffset = new Size(metrics.StackingOffsetOnFaceUpCard.Width / 2, 0);

            // ReSharper disable PossibleNullReferenceException
            var points = new Size[pile.Count];
            // ReSharper restore PossibleNullReferenceException

            var tableauCount = PilesNamed(AnyTableau).Count();

            for (var i = 1; i < pile.Count; ++i)
            {
                var offset = i % tableauCount == 0 ? stackingOffset : Size.Empty;
                points[i] = points[i - 1] + offset;
            }
            
            return points;
        }
    }
}